/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.persistence.criteria;

import com.genia.toolbox.persistence.criteria.criterion.Criterion;
import com.genia.toolbox.persistence.criteria.field.Field;
import com.genia.toolbox.persistence.criteria.order.Order;

/**
 * this is the interface for defining criteria object, and encapuslates all
 * needed features for specifying query using criteria.
 * 
 * @param <MAINTYPE>
 *          the main type of objects to retrieve
 */
public interface Criteria<MAINTYPE>
{

  /**
   * add an order element to the criteria.
   * 
   * @param order
   *          an Order object that specifying the property and the type of order
   *          to perform, use the Orders factory bean for easily specifying the
   *          desired order
   * @return a reference to this criteria for facilitating the query
   *         specification
   */
  public Criteria<MAINTYPE> addOrder(Order order);



  /**
   * Add a field to the results of this query.
   * 
   * @param <TYPE>
   *          the type of the field to add
   * @param type
   *          the class of the type of the field to add
   * @param field
   *          the field to add
   * @return the {@link CriteriaType} to be used to refer to the new field in
   *         the query
   */
  public <TYPE> CriteriaType<TYPE> addProjection(Class<TYPE> type, Field field);



  /**
   * Create a new {@link Criteria} that is the projection of this
   * {@link Criteria} on the {@link Field} passed in argument.
   * 
   * @param <TYPE>
   *          the type of the field to project to
   * @param type
   *          the class of the type of the field to project to
   * @param field
   *          the field to project to
   * @return the new {@link Criteria} representing the projected query.
   */
  public <TYPE> Criteria<TYPE> project(Class<TYPE> type, Field field);



  /**
   * Add a field to the results of this query. This method is used only to add a
   * type that has been created with <code>join</code>.
   * 
   * @param criteriaType
   *          the {@link CriteriaType} of the field to add in the query. This
   *          {@link CriteriaType} must already be an existing
   *          {@link CriteriaType} of this criteria.
   */
  public void addProjection(CriteriaType<?> criteriaType);



  /**
   * Create a new {@link Criteria} that is the projection of this
   * {@link Criteria} on the object defined by a {@link CriteriaType}. This
   * method is used only to project to a type that has been created with
   * <code>join</code>.
   * 
   * @param <TYPE>
   *          the type of the field to project to
   * @param criteriaType
   *          the {@link CriteriaType} of the field to add in the query. This
   *          {@link CriteriaType} must already be an existing
   *          {@link CriteriaType} of this criteria.
   * @return the new {@link Criteria} representing the projected query.
   */
  public <TYPE> Criteria<TYPE> project(CriteriaType<TYPE> criteriaType);



  /**
   * add a restriction criterion to the criteria object. The given restriction
   * describes the query to perform.
   * 
   * @param criterion
   *          a criterion to constrain the results to retrieve, use the
   *          Restrictions factory bean for easily specifying criterion
   * @return a reference to this criteria for facilitating the query
   *         specification
   */
  public Criteria<MAINTYPE> addRestriction(Criterion criterion);



  /**
   * return the main type of this criteria. The main type is the type that this
   * criteria was created with.
   * 
   * @return the main type of this criteria
   */
  public CriteriaType<MAINTYPE> getMainType();



  /**
   * add a grouping clause.
   * 
   * @param field
   *          the field to group by
   */
  public void groupBy(Field field);



  /**
   * set wether the query must returns distinct result or not.
   * 
   * @param distinct
   *          <code>true</code> if and only if the query must return distinct
   *          results.
   */
  public void setDistinct(boolean distinct);



  /**
   * Perfrom an inner join of this criteria with the type passed in parameter.
   * 
   * @param <TYPE>
   *          the type of the object with which we do a join
   * @param type
   *          the class of the object with which we do a join
   * @return the {@link CriteriaType} to be used to refer to the new object in
   *         the join
   */
  public <TYPE> CriteriaType<TYPE> join(Class<TYPE> type);



  /**
   * Perfrom an inner join of this criteria with the elements described by the
   * {@link PropertyField} passed in parameter.
   * 
   * @param <TYPE>
   *          the type of the object with which we do a join
   * @param <INNERTYPE>
   *          the type of the elements of the property
   * @param criteriaType
   *          the {@link CriteriaType} describing the object that will be
   *          inspected
   * @param propertyField
   *          the description of the field of the object on which a join will be
   *          done
   * @return the {@link CriteriaType} to be used to refer to the new object in
   *         the join
   */
  public <TYPE, INNERTYPE> CriteriaType<INNERTYPE> join(CriteriaType<?> criteriaType, PropertyField<TYPE, INNERTYPE> propertyField);



  /**
   * Perfrom an inner join of this criteria with the elements described by the
   * {@link PropertyField} passed in parameter.
   * 
   * @param <TYPE>
   *          the type of the object with which we do a join
   * @param <INNERTYPE>
   *          the type of the elements of the property
   * @param propertyField
   *          the description of the field of the main object on which a join
   *          will be done
   * @return the {@link CriteriaType} to be used to refer to the new object in
   *         the join
   */
  public <TYPE, INNERTYPE> CriteriaType<INNERTYPE> join(PropertyField<TYPE, INNERTYPE> propertyField);



  /**
   * returns if this {@link Criteria} is an aggregate.
   * 
   * @return <code>true</code> if this {@link Criteria} is an aggregate,
   *         <code>false</code> otherwise.
   */
  public boolean isAggregate();

}
